﻿using System;
using System.Globalization;
using System.IO;
using System.Text;
using System.Windows.Media.Media3D;
using LFSRecord2.Misc;
using LFSRecord2.Structures;

namespace LFSRecord2.Model
{
    public static class PathExport
    {
        public static void ExportLFSRecordCustomPath(LfsRecordProject project, string filePath)
        {
            // Can we export anything at all?
            int activeCamIndex = project.GetActiveCameraLayerIndex();
            if (activeCamIndex < 0)
            {
                throw new Exception("No active camera in project");
            }

            int lastFrameNum = project.Layers[activeCamIndex].LastKeyFrameNum();
            if (lastFrameNum < 0)
            {
                throw new Exception("Active camera has no animation");
            }
            int curFrameNum = 0;

            // Open file
            FileStream fs;
            BinaryWriter bfs;
            try
            {
                fs = File.OpenWrite(filePath);
                fs.SetLength(0);
                bfs = new BinaryWriter(fs, Encoding.ASCII);
            }
            catch (Exception e)
            {
                throw new Exception("File error", e);
            }

            // Write data
            LfsCamera cam;
            uint numFrames = 0;
            while (curFrameNum < lastFrameNum)
            {
                cam = project.GetActiveCameraAtIndex(curFrameNum);

                bfs.Write((int)(cam.PosX * 65536));
                bfs.Write((int)(-cam.PosY * 65536));
                bfs.Write((int)(cam.PosZ * 65536));

                bfs.Write((ushort)(-cam.Heading * LfsHelper.LfsDegMult));
                bfs.Write((ushort)(cam.Pitch * LfsHelper.LfsDegMult));
                bfs.Write((ushort)(cam.Roll * LfsHelper.LfsDegMult));
                bfs.Write((ushort)0);

                bfs.Write((float)cam.FOV);

                numFrames++;
                curFrameNum++;
            }
            //Debug.WriteLine("Have exported " + numFrames + " frames. Total time was : " + lastFrameNum);

            // Close file
            bfs.Close();
            fs.Close();
        }

        public static void ExportAFXPath(LfsRecordProject project, ExportAEDialogDataTemplate exportSettings, string filePath)
        {
            string splitChar = "\\";
            string[] exp = filePath.Split((char)splitChar[0]);
            int splitPos = exp[exp.Length - 1].LastIndexOf(".", StringComparison.OrdinalIgnoreCase);
            string outFileName = exp[exp.Length - 1].Substring(0, splitPos);

            double fov;
            double zoom;
            string measured = "measured horizontally";

            string afxLayerName;
            CompLayerBase layer;
            CompLayerLookCamera lookCamera;
            CompLayerFreeCamera freeCamera;
            CompLayerNull nullObject;
            CompLayerReplayData replayData;
            Matrix3D parentMatrix;
            //CustomPoint3DKeyFrame kfp;
            //CustomDoubleKeyFrame kfd;
            Point3D p, p2;
            double d;
            using (StreamWriter sw = new StreamWriter(@filePath))
            {
                sw.WriteLine("var lfsrComp = app.project.items.addComp(\"" + outFileName + "\"," + exportSettings.MovieWidth + "," + exportSettings.MovieHeight + ",1,(" + (exportSettings.EndFrame + 1) + "/" + project.FPS + ")," + project.FPS + ")");

                for (int a = 0; a < exportSettings.SelectedLayers.Count; a++)
                {
                    layer = project.GetLayerByName(exportSettings.SelectedLayers[a]);
                    if (layer.LayerType == CompLayerTypes.LookCamera)
                        afxLayerName = "myLookCam" + layer.LayerIndex;
                    else if (layer.LayerType == CompLayerTypes.FreeCamera)
                        afxLayerName = "myFreeCam" + layer.LayerIndex;
                    else if (layer.LayerType == CompLayerTypes.Null)
                        afxLayerName = "myNull" + layer.LayerIndex;
                    else if (layer.LayerType == CompLayerTypes.ReplayData)
                        afxLayerName = "myNull" + layer.LayerIndex;
                    else
                        continue;

                    if (layer.LayerType == CompLayerTypes.LookCamera)
                    {
                        lookCamera = (CompLayerLookCamera)layer;
                        sw.WriteLine("var {0} = lfsrComp.layers.addCamera(\"{1}\", [0,0])", afxLayerName, lookCamera.LayerName);

                        sw.WriteLine("var newMark{0} = new MarkerValue(\"Film Size :{1}mm or {2}px - {3}\", \"\", \"\", \"\", \"\", \"\")", a, (exportSettings.MovieWidth * 25.4f / 72.0f), exportSettings.MovieWidth, measured);
                        sw.WriteLine("{0}.marker.setValue(newMark{1})", afxLayerName, a);

                        // Default / start values
                        p = (Point3D)lookCamera.Properties[(int)CompLayerLookCamera.PropertyTypes.Position].GetValueAtFrame(exportSettings.StartFrame);
                        sw.WriteLine("{0}.position.setValue([{1},{2},{3}])",
                            afxLayerName,
                            p.X.ToString(CultureInfo.InvariantCulture),
                            (-p.Z).ToString(CultureInfo.InvariantCulture),
                            (-p.Y).ToString(CultureInfo.InvariantCulture)
                            );

                        p = (Point3D)lookCamera.Properties[(int)CompLayerLookCamera.PropertyTypes.LookAt].GetValueAtFrame(exportSettings.StartFrame);
                        sw.WriteLine("{0}.pointOfInterest.setValue([{1},{2},{3}])",
                            afxLayerName,
                            p.X.ToString(CultureInfo.InvariantCulture),
                            (-p.Z).ToString(CultureInfo.InvariantCulture),
                            (-p.Y).ToString(CultureInfo.InvariantCulture)
                            );

                        p = (Point3D)lookCamera.Properties[(int)CompLayerLookCamera.PropertyTypes.Orientation].GetValueAtFrame(exportSettings.StartFrame);
                        sw.WriteLine("{0}.orientation.setValue([{1},{2},{3}])",
                            afxLayerName,
                            p.X.ToString(CultureInfo.InvariantCulture),
                            p.Z.ToString(CultureInfo.InvariantCulture),
                            p.Y.ToString(CultureInfo.InvariantCulture)
                            );

                        d = (double)lookCamera.Properties[(int)CompLayerLookCamera.PropertyTypes.Roll].GetValueAtFrame(exportSettings.StartFrame);
                        sw.WriteLine("{0}.zRotation.setValue({1})",
                            afxLayerName,
                            d.ToString(CultureInfo.InvariantCulture)
                            );

                        fov = (double)lookCamera.Properties[(int)CompLayerLookCamera.PropertyTypes.Fov].GetValueAtFrame(exportSettings.StartFrame) * 0.5f;
                        zoom = (0.5 * exportSettings.MovieWidth) / Math.Tan(fov * Math.PI / 180);
                        sw.WriteLine("{0}.zoom.setValue({1})",
                            afxLayerName,
                            zoom.ToString(CultureInfo.InvariantCulture)
                            );

                        // Write baked animation data
                        if (lookCamera.HasParentLayer &&
                            lookCamera.Properties[(int)CompLayerLookCamera.PropertyTypes.LookAt].HasBoundProperty &&
                            lookCamera.Properties[(int)CompLayerLookCamera.PropertyTypes.LookAt].BoundPropertyIgnoreParent)
                        {
                            // Special case where we want to include parented information in our position and point of interest
                            for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                            {
                                parentMatrix = lookCamera.GetParentedTransformMatrixAtFrame(b, false, false);
                                p = parentMatrix.Transform((Point3D)lookCamera.Properties[(int)CompLayerLookCamera.PropertyTypes.Position].GetValueAtFrame(b));
                                sw.WriteLine("{0}.position.setValueAtTime(({1}/{2}),[{3},{4},{5}])",
                                    afxLayerName,
                                    b,
                                    project.FPS,
                                    p.X.ToString(CultureInfo.InvariantCulture),
                                    (-p.Z).ToString(CultureInfo.InvariantCulture),
                                    (-p.Y).ToString(CultureInfo.InvariantCulture)
                                    );
                            }
                        }
                        else
                        {
                            // Write regular data
                            if (lookCamera.Properties[(int)CompLayerLookCamera.PropertyTypes.Position].KeyFrames.Count > 0)
                            {
                                for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                                {
                                    p = (Point3D)lookCamera.Properties[(int)CompLayerLookCamera.PropertyTypes.Position].GetValueAtFrame(b);
                                    sw.WriteLine("{0}.position.setValueAtTime(({1}/{2}),[{3},{4},{5}])",
                                        afxLayerName,
                                        b,
                                        project.FPS,
                                        p.X.ToString(CultureInfo.InvariantCulture),
                                        (-p.Z).ToString(CultureInfo.InvariantCulture),
                                        (-p.Y).ToString(CultureInfo.InvariantCulture)
                                        );
                                }
                            }

                            if (lookCamera.Properties[(int)CompLayerLookCamera.PropertyTypes.LookAt].KeyFrames.Count > 0)
                            {
                                for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                                {
                                    p = (Point3D)lookCamera.Properties[(int)CompLayerLookCamera.PropertyTypes.LookAt].GetValueAtFrame(b);
                                    sw.WriteLine("{0}.pointOfInterest.setValueAtTime(({1}/{2}),[{3},{4},{5}])",
                                        afxLayerName,
                                        b,
                                        project.FPS,
                                        p.X.ToString(CultureInfo.InvariantCulture),
                                        (-p.Z).ToString(CultureInfo.InvariantCulture),
                                        (-p.Y).ToString(CultureInfo.InvariantCulture)
                                        );
                                }
                            }
                        }

                        if (lookCamera.Properties[(int)CompLayerLookCamera.PropertyTypes.Roll].KeyFrames.Count > 0)
                        {
                            for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                            {
                                d = (double)lookCamera.Properties[(int)CompLayerLookCamera.PropertyTypes.Roll].GetValueAtFrame(b);
                                sw.WriteLine("{0}.zRotation.setValueAtTime(({1}/{2}),{3})",
                                    afxLayerName,
                                    b,
                                    project.FPS,
                                    d.ToString(CultureInfo.InvariantCulture)
                                    );
                            }
                        }

                        if (lookCamera.Properties[(int)CompLayerLookCamera.PropertyTypes.Orientation].KeyFrames.Count > 0)
                        {
                            for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                            {
                                p = (Point3D)lookCamera.Properties[(int)CompLayerLookCamera.PropertyTypes.Orientation].GetValueAtFrame(b);
                                sw.WriteLine("{0}.orientation.setValueAtTime(({1}/{2}),[{3},{4},{5}])",
                                    afxLayerName,
                                    b,
                                    project.FPS,
                                    p.X.ToString(CultureInfo.InvariantCulture),
                                    p.Z.ToString(CultureInfo.InvariantCulture),
                                    p.Y.ToString(CultureInfo.InvariantCulture)
                                    );
                            }
                        }

                        if (lookCamera.Properties[(int)CompLayerLookCamera.PropertyTypes.Fov].KeyFrames.Count > 0)
                        {
                            for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                            {
                                fov = (double)lookCamera.Properties[(int)CompLayerLookCamera.PropertyTypes.Fov].GetValueAtFrame(b) * 0.5f;
                                zoom = (0.5 * exportSettings.MovieWidth) / Math.Tan(fov * Math.PI / 180);
                                sw.WriteLine("{0}.zoom.setValueAtTime(({1}/{2}),{3})",
                                    afxLayerName,
                                    b,
                                    project.FPS,
                                    zoom.ToString(CultureInfo.InvariantCulture)
                                    );
                            }
                        }
                    }
                    else if (layer.LayerType == CompLayerTypes.FreeCamera)
                    {
                        freeCamera = (CompLayerFreeCamera)layer;
                        sw.WriteLine("var {0} = lfsrComp.layers.addCamera(\"{1}\", [0,0])", afxLayerName, freeCamera.LayerName);

                        sw.WriteLine("var newMark{0} = new MarkerValue(\"Film Size :{1}mm or {2}px - {3}\", \"\", \"\", \"\", \"\", \"\")", a, (exportSettings.MovieWidth * 25.4f / 72.0f), exportSettings.MovieWidth, measured);
                        sw.WriteLine("{0}.marker.setValue(newMark{1})", afxLayerName, a);

                        // Default / start values
                        p = (Point3D)freeCamera.Properties[(int)CompLayerFreeCamera.PropertyTypes.Position].GetValueAtFrame(exportSettings.StartFrame);
                        sw.WriteLine("{0}.position.setValue([{1},{2},{3}])",
                            afxLayerName,
                            p.X.ToString(CultureInfo.InvariantCulture),
                            (-p.Z).ToString(CultureInfo.InvariantCulture),
                            (-p.Y).ToString(CultureInfo.InvariantCulture)
                            );

                        p2 = MathHelper.Point3DFromHeadingPitch(
                                    new Point3D(0, 100, 0),
                                    -(double)freeCamera.Properties[(int)CompLayerFreeCamera.PropertyTypes.Heading].GetValueAtFrame(exportSettings.StartFrame) + 180,
                                    (double)freeCamera.Properties[(int)CompLayerFreeCamera.PropertyTypes.Pitch].GetValueAtFrame(exportSettings.StartFrame)
                                    );
                        p = new Point3D(
                            p.X + (p2.X),
                            p.Y - (p2.Y),
                            p.Z - (p2.Z)
                            );
                        sw.WriteLine("{0}.pointOfInterest.setValue([{1},{2},{3}])",
                            afxLayerName,
                            p.X.ToString(CultureInfo.InvariantCulture),
                            (-p.Z).ToString(CultureInfo.InvariantCulture),
                            (-p.Y).ToString(CultureInfo.InvariantCulture)
                            );

                        p = (Point3D)freeCamera.Properties[(int)CompLayerFreeCamera.PropertyTypes.Orientation].GetValueAtFrame(exportSettings.StartFrame);
                        sw.WriteLine("{0}.orientation.setValue([{1},{2},{3}])",
                            afxLayerName,
                            p.X.ToString(CultureInfo.InvariantCulture),
                            p.Z.ToString(CultureInfo.InvariantCulture),
                            p.Y.ToString(CultureInfo.InvariantCulture)
                            );

                        d = (double)freeCamera.Properties[(int)CompLayerFreeCamera.PropertyTypes.Roll].GetValueAtFrame(exportSettings.StartFrame);
                        sw.WriteLine("{0}.zRotation.setValue({1})",
                            afxLayerName,
                            d.ToString(CultureInfo.InvariantCulture)
                            );

                        fov = (double)freeCamera.Properties[(int)CompLayerFreeCamera.PropertyTypes.Fov].GetValueAtFrame(exportSettings.StartFrame) * 0.5f;
                        zoom = (0.5 * exportSettings.MovieWidth) / Math.Tan(fov * Math.PI / 180);
                        sw.WriteLine("{0}.zoom.setValue({1})",
                            afxLayerName,
                            zoom.ToString(CultureInfo.InvariantCulture)
                            );

                        // Write baked animation data
                        // Position
                        if (freeCamera.Properties[(int)CompLayerFreeCamera.PropertyTypes.Position].KeyFrames.Count > 0)
                        {
                            for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                            {
                                p = (Point3D)freeCamera.Properties[(int)CompLayerFreeCamera.PropertyTypes.Position].GetValueAtFrame(b);
                                sw.WriteLine("{0}.position.setValueAtTime(({1}/{2}),[{3},{4},{5}])",
                                    afxLayerName,
                                    b,
                                    project.FPS,
                                    p.X.ToString(CultureInfo.InvariantCulture),
                                    (-p.Z).ToString(CultureInfo.InvariantCulture),
                                    (-p.Y).ToString(CultureInfo.InvariantCulture)
                                    );
                            }
                        }

                        // Generated poi
                        if (freeCamera.Properties[(int)CompLayerFreeCamera.PropertyTypes.Heading].KeyFrames.Count > 0 ||
                            freeCamera.Properties[(int)CompLayerFreeCamera.PropertyTypes.Pitch].KeyFrames.Count > 0)
                        {
                            for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                            {
                                p = (Point3D)freeCamera.Properties[(int)CompLayerFreeCamera.PropertyTypes.Position].GetValueAtFrame(b);
                                p2 = MathHelper.Point3DFromHeadingPitch(
                                    new Point3D(0, 100, 0),
                                    -(double)freeCamera.Properties[(int)CompLayerFreeCamera.PropertyTypes.Heading].GetValueAtFrame(b) + 180,
                                    (double)freeCamera.Properties[(int)CompLayerFreeCamera.PropertyTypes.Pitch].GetValueAtFrame(b)
                                    );
                                p = new Point3D(
                                    p.X + (p2.X),
                                    p.Y - (p2.Y),
                                    p.Z - (p2.Z)
                                    );
                                sw.WriteLine("{0}.pointOfInterest.setValueAtTime(({1}/{2}),[{3},{4},{5}])",
                                    afxLayerName,
                                    b,
                                    project.FPS,
                                    p.X.ToString(CultureInfo.InvariantCulture),
                                    (-p.Z).ToString(CultureInfo.InvariantCulture),
                                    (-p.Y).ToString(CultureInfo.InvariantCulture)
                                    );
                            }
                        }

                        // Roll
                        if (freeCamera.Properties[(int)CompLayerFreeCamera.PropertyTypes.Roll].KeyFrames.Count > 0)
                        {
                            for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                            {
                                d = (double)freeCamera.Properties[(int)CompLayerFreeCamera.PropertyTypes.Roll].GetValueAtFrame(b);
                                sw.WriteLine("{0}.zRotation.setValueAtTime(({1}/{2}),{3})",
                                    afxLayerName,
                                    b,
                                    project.FPS,
                                    d.ToString(CultureInfo.InvariantCulture)
                                    );
                            }
                        }

                        if (freeCamera.Properties[(int)CompLayerFreeCamera.PropertyTypes.Orientation].KeyFrames.Count > 0)
                        {
                            for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                            {
                                p = (Point3D)freeCamera.Properties[(int)CompLayerFreeCamera.PropertyTypes.Orientation].GetValueAtFrame(b);
                                sw.WriteLine("{0}.orientation.setValueAtTime(({1}/{2}),[{3},{4},{5}])",
                                    afxLayerName,
                                    b,
                                    project.FPS,
                                    p.X.ToString(CultureInfo.InvariantCulture),
                                    p.Z.ToString(CultureInfo.InvariantCulture),
                                    p.Y.ToString(CultureInfo.InvariantCulture)
                                    );
                            }
                        }

                        // FOV
                        if (freeCamera.Properties[(int)CompLayerFreeCamera.PropertyTypes.Fov].KeyFrames.Count > 0)
                        {
                            for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                            {
                                fov = (double)freeCamera.Properties[(int)CompLayerFreeCamera.PropertyTypes.Fov].GetValueAtFrame(b) * 0.5f;
                                zoom = (0.5 * exportSettings.MovieWidth) / Math.Tan(fov * Math.PI / 180);
                                sw.WriteLine("{0}.zoom.setValueAtTime(({1}/{2}),{3})",
                                    afxLayerName,
                                    b,
                                    project.FPS,
                                    zoom.ToString(CultureInfo.InvariantCulture)
                                    );
                            }
                        }
                    }
                    else if (layer.LayerType == CompLayerTypes.Null)
                    {
                        nullObject = (CompLayerNull)layer;
                        sw.WriteLine("var {0} = lfsrComp.layers.addNull({1})", afxLayerName, exportSettings.EndFrame);
                        sw.WriteLine("{0}.name = \"{1}\"", afxLayerName, nullObject.LayerName);
                        sw.WriteLine("{0}.threeDLayer = true", afxLayerName);

                        // Default / start values
                        p = (Point3D)nullObject.Properties[(int)CompLayerNull.PropertyTypes.Position].GetValueAtFrame(exportSettings.StartFrame);
                        sw.WriteLine("{0}.position.setValue([{1},{2},{3}])",
                            afxLayerName,
                            p.X.ToString(CultureInfo.InvariantCulture),
                            (-p.Z).ToString(CultureInfo.InvariantCulture),
                            (-p.Y).ToString(CultureInfo.InvariantCulture)
                            );
                        p = (Point3D)nullObject.Properties[(int)CompLayerNull.PropertyTypes.Anchor].GetValueAtFrame(exportSettings.StartFrame);
                        sw.WriteLine("{0}.anchorPoint.setValue([{1},{2},{3}])",
                            afxLayerName,
                            p.X.ToString(CultureInfo.InvariantCulture),
                            (-p.Z).ToString(CultureInfo.InvariantCulture),
                            (-p.Y).ToString(CultureInfo.InvariantCulture)
                            );
                        p = (Point3D)nullObject.Properties[(int)CompLayerNull.PropertyTypes.Scale].GetValueAtFrame(exportSettings.StartFrame);
                        sw.WriteLine("{0}.scale.setValue([{1},{2},{3}])",
                            afxLayerName,
                            (p.X * 100).ToString(CultureInfo.InvariantCulture),
                            (p.Z * 100).ToString(CultureInfo.InvariantCulture),
                            (p.Y * 100).ToString(CultureInfo.InvariantCulture)
                            );
                        p = (Point3D)nullObject.Properties[(int)CompLayerNull.PropertyTypes.Orientation].GetValueAtFrame(exportSettings.StartFrame);
                        sw.WriteLine("{0}.orientation.setValue([{1},{2},{3}])",
                            afxLayerName,
                            p.X.ToString(CultureInfo.InvariantCulture),
                            p.Z.ToString(CultureInfo.InvariantCulture),
                            p.Y.ToString(CultureInfo.InvariantCulture)
                            );
                        d = (double)nullObject.Properties[(int)CompLayerNull.PropertyTypes.RotateX].GetValueAtFrame(exportSettings.StartFrame);
                        sw.WriteLine("{0}.xRotation.setValue({1})",
                            afxLayerName,
                            d.ToString(CultureInfo.InvariantCulture)
                            );
                        d = (double)nullObject.Properties[(int)CompLayerNull.PropertyTypes.RotateZ].GetValueAtFrame(exportSettings.StartFrame);
                        sw.WriteLine("{0}.yRotation.setValue({1})",
                            afxLayerName,
                            d.ToString(CultureInfo.InvariantCulture)
                            );
                        d = (double)nullObject.Properties[(int)CompLayerNull.PropertyTypes.RotateY].GetValueAtFrame(exportSettings.StartFrame);
                        sw.WriteLine("{0}.zRotation.setValue({1})",
                            afxLayerName,
                            d.ToString(CultureInfo.InvariantCulture)
                            );

                        // Write baked animation data
                        if (nullObject.Properties[(int)CompLayerNull.PropertyTypes.Position].KeyFrames.Count > 0)
                        {
                            for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                            {
                                p = (Point3D)nullObject.Properties[(int)CompLayerNull.PropertyTypes.Position].GetValueAtFrame(b);
                                sw.WriteLine("{0}.position.setValueAtTime(({1}/{2}),[{3},{4},{5}])",
                                    afxLayerName,
                                    b,
                                    project.FPS,
                                    p.X.ToString(CultureInfo.InvariantCulture),
                                    (-p.Z).ToString(CultureInfo.InvariantCulture),
                                    (-p.Y).ToString(CultureInfo.InvariantCulture)
                                    );
                            }
                        }

                        if (nullObject.Properties[(int)CompLayerNull.PropertyTypes.Anchor].KeyFrames.Count > 0)
                        {
                            for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                            {
                                p = (Point3D)nullObject.Properties[(int)CompLayerNull.PropertyTypes.Anchor].GetValueAtFrame(b);
                                sw.WriteLine("{0}.anchorPoint.setValueAtTime(({1}/{2}),[{3},{4},{5}])",
                                    afxLayerName,
                                    b,
                                    project.FPS,
                                    p.X.ToString(CultureInfo.InvariantCulture),
                                    (-p.Z).ToString(CultureInfo.InvariantCulture),
                                    (-p.Y).ToString(CultureInfo.InvariantCulture)
                                    );
                            }
                        }
                        if (nullObject.Properties[(int)CompLayerNull.PropertyTypes.Scale].KeyFrames.Count > 0)
                        {
                            for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                            {
                                p = (Point3D)nullObject.Properties[(int)CompLayerNull.PropertyTypes.Scale].GetValueAtFrame(b);
                                sw.WriteLine("{0}.scale.setValueAtTime(({1}/{2}),[{3},{4},{5}])",
                                    afxLayerName,
                                    b,
                                    project.FPS,
                                    (p.X * 100).ToString(CultureInfo.InvariantCulture),
                                    (p.Z * 100).ToString(CultureInfo.InvariantCulture),
                                    (p.Y * 100).ToString(CultureInfo.InvariantCulture)
                                    );
                            }
                        }
                        if (nullObject.Properties[(int)CompLayerNull.PropertyTypes.Orientation].KeyFrames.Count > 0)
                        {
                            for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                            {
                                p = (Point3D)nullObject.Properties[(int)CompLayerNull.PropertyTypes.Orientation].GetValueAtFrame(b);
                                sw.WriteLine("{0}.orientation.setValueAtTime(({1}/{2}),[{3},{4},{5}])",
                                    afxLayerName,
                                    b,
                                    project.FPS,
                                    p.X.ToString(CultureInfo.InvariantCulture),
                                    p.Z.ToString(CultureInfo.InvariantCulture),
                                    p.Y.ToString(CultureInfo.InvariantCulture)
                                    );
                            }
                        }
                        if (nullObject.Properties[(int)CompLayerNull.PropertyTypes.RotateX].KeyFrames.Count > 0)
                        {
                            for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                            {
                                d = (double)nullObject.Properties[(int)CompLayerNull.PropertyTypes.RotateX].GetValueAtFrame(b);
                                sw.WriteLine("{0}.xRotation.setValueAtTime(({1}/{2}),{3})",
                                    afxLayerName,
                                    b,
                                    project.FPS,
                                    d.ToString(CultureInfo.InvariantCulture)
                                    );
                            }
                        }
                        if (nullObject.Properties[(int)CompLayerNull.PropertyTypes.RotateZ].KeyFrames.Count > 0)
                        {
                            for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                            {
                                d = (double)nullObject.Properties[(int)CompLayerNull.PropertyTypes.RotateZ].GetValueAtFrame(b);
                                sw.WriteLine("{0}.yRotation.setValueAtTime(({1}/{2}),{3})",
                                    afxLayerName,
                                    b,
                                    project.FPS,
                                    d.ToString(CultureInfo.InvariantCulture)
                                    );
                            }
                        }
                        if (nullObject.Properties[(int)CompLayerNull.PropertyTypes.RotateY].KeyFrames.Count > 0)
                        {
                            for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                            {
                                d = (double)nullObject.Properties[(int)CompLayerNull.PropertyTypes.RotateY].GetValueAtFrame(b);
                                sw.WriteLine("{0}.zRotation.setValueAtTime(({1}/{2}),{3})",
                                    afxLayerName,
                                    b,
                                    project.FPS,
                                    d.ToString(CultureInfo.InvariantCulture)
                                    );
                            }
                        }
                    }
                    else if (layer.LayerType == CompLayerTypes.ReplayData)
                    {
                        // ReplayData layers only have position and heading
                        replayData = (CompLayerReplayData)layer;
                        sw.WriteLine("var {0} = lfsrComp.layers.addNull({1})", afxLayerName, exportSettings.EndFrame);
                        sw.WriteLine("{0}.name = \"{1}\"", afxLayerName, replayData.LayerName);
                        sw.WriteLine("{0}.threeDLayer = true", afxLayerName);

                        // Default / start values
                        p = (Point3D)replayData.Properties[(int)CompLayerReplayData.PropertyTypes.Position].GetValueAtFrame(exportSettings.StartFrame);
                        sw.WriteLine("{0}.position.setValue([{1},{2},{3}])",
                            afxLayerName,
                            p.X.ToString(CultureInfo.InvariantCulture),
                            (-p.Z).ToString(CultureInfo.InvariantCulture),
                            (-p.Y).ToString(CultureInfo.InvariantCulture)
                            );

                        d = (double)replayData.Properties[(int)CompLayerReplayData.PropertyTypes.Heading].GetValueAtFrame(exportSettings.StartFrame);
                        sw.WriteLine("{0}.yRotation.setValue({1})",
                            afxLayerName,
                            d.ToString(CultureInfo.InvariantCulture)
                            );

                        // Write baked animation data
                        for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                        {
                            p = (Point3D)replayData.Properties[(int)CompLayerReplayData.PropertyTypes.Position].GetValueAtFrame(b);
                            sw.WriteLine("{0}.position.setValueAtTime(({1}/{2}),[{3},{4},{5}])",
                                afxLayerName,
                                b,
                                project.FPS,
                                p.X.ToString(CultureInfo.InvariantCulture),
                                (-p.Z).ToString(CultureInfo.InvariantCulture),
                                (-p.Y).ToString(CultureInfo.InvariantCulture)
                                );
                        }
                        for (int b = exportSettings.StartFrame; b <= exportSettings.EndFrame; b++)
                        {
                            d = (double)replayData.Properties[(int)CompLayerReplayData.PropertyTypes.Heading].GetValueAtFrame(b);
                            sw.WriteLine("{0}.yRotation.setValueAtTime(({1}/{2}),{3})",
                                afxLayerName,
                                b,
                                project.FPS,
                                d.ToString(CultureInfo.InvariantCulture)
                                );
                        }
                    }
                }

                // Add any property bindings. We couldn't do that earlier, because they might link to layers not yet added to the script (back then).
                for (int a = 0; a < exportSettings.SelectedLayers.Count; a++)
                {
                    layer = project.GetLayerByName(exportSettings.SelectedLayers[a]);

                    if (layer.LayerType == CompLayerTypes.LookCamera)
                        afxLayerName = "myLookCam" + layer.LayerIndex;
                    else if (layer.LayerType == CompLayerTypes.FreeCamera)
                        afxLayerName = "myFreeCam" + layer.LayerIndex;
                    else if (layer.LayerType == CompLayerTypes.Null)
                        afxLayerName = "myNull" + layer.LayerIndex;
                    else if (layer.LayerType == CompLayerTypes.ReplayData)
                        afxLayerName = "myNull" + layer.LayerIndex;
                    else
                        continue;

                    for (int b = 0; b < layer.Properties.Count; b++)
                    {
                        if (layer.Properties[b].HasBoundProperty && layer.Properties[b].PropertyName != "Time Offset" && layer.Properties[b].PropertyName != "Data Source")
                        {
                            sw.WriteLine("{0}.{1}.expression = \"thisComp.layer(\\\"{2}\\\").transform.{3}\"",
                                afxLayerName,
                                ConvertPropertyNameToAFXName(layer.Properties[b].PropertyName),
                                layer.Properties[b].BoundProperty.ParentLayer.LayerName,
                                ConvertPropertyNameToAFXName(layer.Properties[b].BoundProperty.PropertyName)
                                );
                        }
                    }

                    if (layer.HasParentLayer)
                    {
                        if (layer.LayerType == CompLayerTypes.LookCamera &&
                            layer.Properties[(int)CompLayerLookCamera.PropertyTypes.LookAt].HasBoundProperty &&
                            layer.Properties[(int)CompLayerLookCamera.PropertyTypes.LookAt].BoundPropertyIgnoreParent)
                        {
                            continue;
                        }    
                        
                        if (layer.ParentLayer.LayerType == CompLayerTypes.LookCamera)
                            sw.WriteLine("{0}.setParentWithJump(myLookCam{1})", afxLayerName, layer.ParentLayer.LayerIndex);
                        else if (layer.ParentLayer.LayerType == CompLayerTypes.FreeCamera)
                            sw.WriteLine("{0}.setParentWithJump(myFreeCam{1})", afxLayerName, layer.ParentLayer.LayerIndex);
                        else if (layer.ParentLayer.LayerType == CompLayerTypes.Null)
                            sw.WriteLine("{0}.setParentWithJump(myNull{1})", afxLayerName, layer.ParentLayer.LayerIndex);
                        else if (layer.ParentLayer.LayerType == CompLayerTypes.ReplayData)
                            sw.WriteLine("{0}.setParentWithJump(myNull{1})", afxLayerName, layer.ParentLayer.LayerIndex);
                    }
                }
            }
        }

        private static string ConvertPropertyNameToAFXName(string propertyName)
        {
            switch (propertyName)
            {
                case "Anchor":
                    return "anchorPoint";
                case "Scale":
                    return "scale";
                case "Position":
                    return "position";
                case "Look At":
                    return "pointOfInterest";
                //case "Heading":
                //    return "yRotation";
                //case "Pitch":
                //    return "xRotation";
                case "Roll":
                    return "zRotation";
                case "FOV":
                    return "zoom";
                case "Orientation":
                    return "orientation";
                case "Rotate X":
                    return "xRotation";
                case "Rotate Y":
                    return "zRotation";
                case "Rotate Z":
                    return "yRotation";
                default:
                    return "unknown";
            }
        }
    }
}
